package gat1400

import (
	"encoding/json"
	"fmt"
	"net/http"

	"gitee.com/general252/gat1400/viid"
	"github.com/go-resty/resty/v2"
)

type Client struct {
	host     string
	port     int
	deviceId viid.DeviceIDType // 13030421191190201061
	password viid.PasswordType

	client *resty.Client
}

func NewClient(host string, port int, deviceId viid.DeviceIDType, password viid.PasswordType) *Client {
	c := &Client{
		host:     host,
		port:     port,
		deviceId: deviceId,
		password: password,
	}

	c.client = resty.NewWithClient(http.DefaultClient)

	return c
}

func (tis *Client) getURI(path string) string {
	return fmt.Sprintf("http://%v:%v%v", tis.host, tis.port, path)
}

func (tis *Client) GetDeviceID() string {
	return string(tis.deviceId)
}

func (tis *Client) Http(method string, path string, body []byte, options ...func(r *resty.Request)) (*resty.Response, error) {
	var (
		deviceId = tis.GetDeviceID()
		uri      = tis.getURI(path)
	)

	r := tis.client.R().EnableTrace()

	r.SetHeader("Content-Type", "application/VIID+JSON")
	r.SetHeader("User-Identify", deviceId)

	if body != nil {
		r.SetBody(body)
	}

	switch method {
	case http.MethodGet:
		return r.Get(uri)
	case http.MethodPost:
		return r.Post(uri)
	case http.MethodPut:
		return r.Put(uri)
	case http.MethodDelete:
		return r.Delete(uri)
	default:
		return nil, fmt.Errorf("error method: %v", method)
	}

}

func (tis *Client) Register(param *viid.RegisterObject) (*viid.ResponseStatusObject, error) {
	var (
		deviceId = tis.GetDeviceID()
		password = string(tis.password)
		uriPath  = "/VIID/System/Register"
		uri      = tis.getURI(uriPath)
	)

	body, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}

	r := tis.client.R().EnableTrace()

	r.SetHeader("Content-Type", "application/json; charset=UTF-8")
	r.SetHeader("User-Identify", deviceId)
	r.SetDigestAuth(deviceId, password)

	reply, err := r.SetBody(body).Post(uri)
	if err != nil {
		return nil, err
	} else if reply.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("%v", reply.Status())
	}

	var object viid.ResponseStatusObject
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		return nil, err
	}

	return &object, nil

}

func (tis *Client) UnRegister(param *viid.UnRegisterObject) (*viid.ResponseStatusObject, error) {
	var (
		deviceId = tis.GetDeviceID()
		password = string(tis.password)

		path = "/VIID/System/UnRegister"
		uri  = tis.getURI(path)
	)

	body, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}

	r := tis.client.R().EnableTrace()

	r.SetHeader("Content-Type", "application/json; charset=UTF-8")
	r.SetHeader("User-Identify", deviceId)
	r.SetDigestAuth(deviceId, password)

	reply, err := r.SetBody(body).Post(uri)
	if err != nil {
		return nil, err
	} else if reply.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("%v", reply.Status())
	}

	var object viid.ResponseStatusObject
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		return nil, err
	}

	return &object, nil
}

func (tis *Client) Keepalive(param *viid.KeepaliveObject) (*viid.ResponseStatusObject, error) {
	const path = "/VIID/System/Keepalive"

	body, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}

	reply, err := tis.Http(http.MethodPost, path, body)
	if err != nil {
		return nil, err
	} else if reply.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("%v", reply.Status())
	}

	var object viid.ResponseStatusObject
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		return nil, err
	}

	return &object, nil
}

func (tis *Client) SystemTime() (*viid.SystemTimeObject, error) {
	const path = "/VIID/System/Time"

	reply, err := tis.Http(http.MethodGet, path, nil)
	if err != nil {
		return nil, err
	} else if reply.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("%v", reply.Status())
	}

	var object viid.SystemTimeObject
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		return nil, err
	}

	return &object, nil
}

func (tis *Client) AddFaces(param *viid.FaceListObject) (*viid.ResponseStatusListObject, error) {
	const path = "/VIID/Faces"

	body, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}

	reply, err := tis.Http(http.MethodPost, path, body)
	if err != nil {
		return nil, err
	} else if reply.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("%v", reply.Status())
	}

	var object viid.ResponseStatusListObject
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		return nil, err
	}

	return &object, nil
}

func (tis *Client) AddPerson(param *viid.PersonListObject) (*viid.ResponseStatusListObject, error) {
	const path = "/VIID/Persons"

	body, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}

	reply, err := tis.Http(http.MethodPost, path, body)
	if err != nil {
		return nil, err
	} else if reply.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("%v", reply.Status())
	}

	var object viid.ResponseStatusListObject
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		return nil, err
	}

	return &object, nil
}

func (tis *Client) AddMotorVehicle(param *viid.MotorVehicleListObject) (*viid.ResponseStatusListObject, error) {
	const path = "/VIID/MotorVehicles"

	body, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}

	reply, err := tis.Http(http.MethodPost, path, body)
	if err != nil {
		return nil, err
	} else if reply.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("%v", reply.Status())
	}

	var object viid.ResponseStatusListObject
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		return nil, err
	}

	return &object, nil
}

func (tis *Client) AddNonMotorVehicle(param *viid.NonMotorVehicleListObject) (*viid.ResponseStatusListObject, error) {
	const path = "/VIID/NonMotorVehicles"

	body, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}

	reply, err := tis.Http(http.MethodPost, path, body)
	if err != nil {
		return nil, err
	} else if reply.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("%v", reply.Status())
	}

	var object viid.ResponseStatusListObject
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		return nil, err
	}

	return &object, nil
}

func (tis *Client) AddFiles(param *viid.FileListObject) (*viid.ResponseStatusListObject, error) {
	const path = "/VIID/Files"

	body, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}

	reply, err := tis.Http(http.MethodPost, path, body)
	if err != nil {
		return nil, err
	} else if reply.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("%v", reply.Status())
	}

	var object viid.ResponseStatusListObject
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		return nil, err
	}

	return &object, nil
}

func (tis *Client) AddImages(param *viid.ImageListObject) (*viid.ResponseStatusListObject, error) {
	const path = "/VIID/Images"

	body, err := json.Marshal(param)
	if err != nil {
		return nil, err
	}

	reply, err := tis.Http(http.MethodPost, path, body)
	if err != nil {
		return nil, err
	} else if reply.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("%v", reply.Status())
	}

	var object viid.ResponseStatusListObject
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		return nil, err
	}

	return &object, nil
}

func (tis *Client) Login(username string, password string) (token string, err error) {
	const path = "/login"

	type Param struct {
		Username  string `json:"username"`
		Password  string `json:"password"`
		Captcha   string `json:"captcha"`
		CaptchaId int    `json:"captcha_id"`
	}
	type LoginReply struct {
		Token   string `json:"token"`
		Expires int    `json:"expires"`
		User    struct {
			Id       int    `json:"id"`
			Username string `json:"username"`
			RgroupId int    `json:"rgroup_id"`
			Name     string `json:"name"`
		} `json:"user"`
		ResetPassword bool `json:"reset_password"`
	}

	body, _ := json.Marshal(Param{
		Username:  username,
		Password:  password,
		Captcha:   "12",
		CaptchaId: 44,
	})

	reply, err := tis.Http(http.MethodPost, path, body)
	if err != nil {
		return "", err
	}

	var object LoginReply
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		return "", err
	}

	return object.Token, nil

}

// GetPersons 错误
func (tis *Client) GetPersons() (*viid.APEStatusListObject, error) {
	const path = "/VIID/Persons?(MaxNumRecordReturn=10)&(PageRecordNum=25)&(RecordStartNo=1)"

	reply, err := tis.Http(http.MethodGet, path, nil, func(r *resty.Request) {
		r.SetHeader("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVSUQiOjIyMiwiVXNlcm5hbWUiOiJhZG1pbiIsIkdyb3VwSUQiOjAsIkdyb3VwTGV2ZWwiOjAsImlzcyI6Inh4QGdvbGFuZy5zcGFjZSIsImV4cCI6MTcwOTg5MDgxMywiaWF0IjoxNzA5ODgzNjEzfQ.fN0KVJWcMo3617tjQ8EKOR6t40U-OQAeZm3DTQtWw_U")
	})
	if err != nil {
		return nil, err
	} else if reply.StatusCode() != http.StatusOK {
		return nil, fmt.Errorf("%v", reply.Status())
	}

	var object viid.APEStatusListObject
	if err = json.Unmarshal(reply.Body(), &object); err != nil {
		return nil, err
	}

	return &object, nil
}
